package org.chipu.shared.util.singletons;

import java.io.*;
import java.text.MessageFormat;
import java.util.Date;
import java.util.logging.*;

public final class LoggingUtils {
	private static Logger logger;
	public static final Formatter myFormatter = new AlignedFormatter();

	public static final Level noticeLevel = new MyLevel();
	@SuppressWarnings("serial")
	private static final class MyLevel extends Level {
		private MyLevel() {
			super("NOTICE", 250);
		}
	}

	private LoggingUtils() {/**/}

	private static String getSpaces(int nSpaces) {
		StringBuilder s = new StringBuilder();
		for(int i = 0; i < nSpaces; i++)
			s.append(' ');
		return s.toString();
	}

	public static Handler createMyConsoleHandler() {
		return new LoggingUtils.MyConsoleHandler(Level.WARNING, Level.FINEST, LoggingUtils.myFormatter);
	}

	public static class MyConsoleHandler extends Handler {
		private final Level errorLevel;

		public MyConsoleHandler(Level errorLevel, Level minimumLevel, Formatter formatter) {
			setLevel(minimumLevel);
			setFormatter(formatter);
			this.errorLevel = errorLevel;
		}

		@Override
		public void publish(LogRecord record) {
			if (record == null)
				return;
			PrintStream os = null;
			if (record.getLevel().intValue() >= errorLevel.intValue())
				os = System.err;
			else if (record.getLevel().intValue() >= getLevel().intValue())
				os = System.out;
			if (os == null)
				return;
			String msg;
			try {
				msg = getFormatter().format(record);
			} catch(Exception ex) {
				reportError(null, ex, ErrorManager.FORMAT_FAILURE);
				return;
			}
			try {
				os.print(msg);
			} catch(Exception ex) {
				reportError(null, ex, ErrorManager.WRITE_FAILURE);
			}
		}

		@Override
		public void close() throws SecurityException {
			flush();
		}

		@Override
		public void flush() {
			System.err.flush();
			System.out.flush();
		}
	}

	public static void setLevel(Logger logger, Level level) {
		logger.setLevel(level);
		for(Handler hh : logger.getHandlers())
			hh.setLevel(level);
	}

	public static Logger getLogger() {
		if (logger == null) {
			logger = Logger.getLogger("org.chipu.jnids.JNIDS");
			try {
				logger.setUseParentHandlers(false);
				logger.addHandler(createMyConsoleHandler());
				setLevel(logger, Level.ALL);
			} catch(SecurityException ex) {
				logger.warning("Could not configure logger (Access denied)");
			}
		}
		return logger;
	}

	public static void setLogger(Logger logger_) {
		logger = logger_;
	}

	public static boolean hasBeenConfigured(Logger logger) {
		for(Handler h : logger.getHandlers())
			if (h instanceof MyConsoleHandler)
				return true;
		return false;
	}

	//IMPROVE see what is more performant
	//It is public because it is used inside the server logging, specifying the classname
	public static class AlignedFormatter extends Formatter {
		private final Date dat = new Date();
		private final Object[] args = new Object[1];
		private MessageFormat formatter;
		// private String lineSeparator = (String) AccessController.doPrivileged(new GetPropertyAction("line.separator"));
		private static final byte maxMethodLength = 25;

		@Override
		public String format(LogRecord record) {
			StringBuilder sb = new StringBuilder();
			// Minimize memory allocations here
			dat.setTime(record.getMillis());
			args[0] = dat;
			StringBuffer text = new StringBuffer();
			if (formatter == null)
				formatter = new MessageFormat("{0,date} {0,time}");
			formatter.format(args, text, null);
			sb.append(text).append("  ");
			//SimpleDateFormat sdf = new SimpleDateFormat("MM/dd HH:mm:ss");
			//sb.append(sdf.format(new Date(record.getMillis()))).append("  ");

			StringBuilder sb2 = new StringBuilder();
			if (record.getSourceClassName() != null)
				sb2.append(record.getSourceClassName().substring(record.getSourceClassName().lastIndexOf('.') + 1));
			else
				sb2.append(record.getLoggerName());

			if (record.getSourceMethodName() != null)
				sb2.append('.').append(record.getSourceMethodName());
			if (sb2.length() < maxMethodLength + 1)
				sb.append(sb2).append(getSpaces(maxMethodLength - sb2.length()));
			else
				sb.append(sb2.substring(0, maxMethodLength - 1)).append('~');
			//String className = record.getLoggerName();
			//int classNameLength = className.length();
			//int before = sb.length();
			//if (classNameLength > maxMethodLength) {
			//	int index = -1;
			//	while(true) {
			//		sb.append(className.charAt(index + 1));
			//		int oldIndex = index;
			//		index = className.indexOf(".", index + 1);
			//		if (index == -1) {
			//			String str = className.substring(oldIndex + 2);
			//			int rem = maxMethodLength - (sb.length() - before);
			//			if (str.length() > rem)
			//				str = str.substring(0, rem - 1) + '~';
			//			sb.append(str);
			//			break;
			//		}
			//		sb.append('.');
			//	}
			//} else
			//	sb.append(className);
			//for(int i = (sb.length() - before); i <= maxMethodLength - 1; i++)
			//	sb.append(' ');

			String name = record.getLevel().getName();
			sb.append('\t').append(name.substring(0, Math.min(7, name.length()))).append('\t');
			sb.append(formatMessage(record)).append("\r\n");
			//if (record.getParameters() != null && record.getParameters().length > 0) {
			//	java.util.Formatter formatter = new java.util.Formatter(sb);
			//	formatter.format(record.getMessage(), record.getParameters());
			//	formatter.format("\n");
			//} else
			//	sb.append(record.getMessage()).append("\r\n");

			if (record.getThrown() != null) {
				StringWriter sw = new StringWriter();
				PrintWriter pw = new PrintWriter(sw);
				record.getThrown().printStackTrace(pw);
				pw.close();
				sb.append(sw.toString());
			}
			return sb.toString();
		}
	}
}